package zdream.pmfield.stage.object;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import zdream.pmfield.stage.Stage;
import zdream.pmfield.stage.action.StageAction;
import zdream.pmfield.stage.buff.seat.SpaceBuff;
import zdream.pmfield.stage.buff.seat.SeatStageBuff;
import zdream.pmfield.stage.buff.StageBuff;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.director.IHookStorage;
import zdream.utils.StringUtils;

/**
 * @author Zdream
 * @date 2023-11-10
 * @since 0.0.1
 */
public class Seat implements IStageTarget, IHookStorage {
	/**
	 * id 只在一个 stage 中不重复
	 * 注意: id 相邻不等于座位相邻
	 */
	public final int id;
	public transient final Stage stage;

	public Seat(int id, Stage stage) {
		this.stage = Objects.requireNonNull(stage);
		this.id = id;
		this.addBuff(new SpaceBuff(this));
	}

	public void init() {}

	@Override
	public Stage getStage() {
		return stage;
	}

	/* **********
	 * Location *
	 ********** */

	public transient Seat left, right;
	public boolean global;
	public int x;

	public void objectArrive(ObjectOnSeat object) {
		objects.add(object);
	}

	public void objectLeave(ObjectOnSeat object) {
		objects.remove(object);
	}

	/**
	 * @param left can be null
	 */
	public void leftJoin(Seat left) {
		this.left = left;
		if (left != null) {
			left.right = this;
		}
	}

	/**
	 * @param right can be null
	 */
	public void rightJoin(Seat right) {
		this.right = right;
		if (right != null) {
			right.left = this;
		}
	}

	public int distance(Seat another) {
		return Math.abs(another.x - x);
	}

	/* **********
	 * Objects  *
	 ********** */

	public final List<ObjectOnSeat> objects = new ArrayList<>();

	public List<FoeActor> findFoes() {
		if (objects.isEmpty()) {
			return Collections.emptyList();
		}

		return objects.stream()
				.filter(o -> o instanceof FoeActor)
				.map(o -> (FoeActor) o)
				.collect(Collectors.toList());
	}

	/* **********
	 *  Buffs   *
	 ********** */

	public static final String BUFF_SPACE = "space";

	public final List<SeatStageBuff> buffs = new ArrayList<>();
	protected final transient HookStore hookStore = new HookStore();

	@Override
	public HookStore.HookHandlerBuilder registerHook() {
		return hookStore.hookBuilder();
	}

	@Override
	public Hooks.HookContext triggerHook(HookType type) {
		return Hooks.triggerHook(type, this.hookStore);
	}

	@Override
	public HookStore getHookStore() {
		return hookStore;
	}

	public SpaceBuff findSpaceBuff() {
		for (SeatStageBuff buff : buffs) {
			if (buff.is(BUFF_SPACE)) {
				return (SpaceBuff) buff;
			}
		}
		throw new RuntimeException("无法寻找到 SpaceBuff");
	}

	public ReceiveBuffReport receiveBuff(SeatStageBuff buff) {
		if (buff.target != this) {
			throw new IllegalArgumentException("buff.seat is " + buff.target + ", not me! I am " + this);
		}

		final Hooks.BeforeSeatReceiveBuffContext context = triggerHook(HookType.BEFORE_SEAT_RECEIVE_BUFF).as();
		context.withOtherBuff(buff).run();
		if (context.aborted) {
			return ReceiveBuffReport.ofReject(buff, context.abortBy.owner);
		}

		addBuff(buff);
		return ReceiveBuffReport.ofAccept(buff);
	}

	protected void addBuff(SeatStageBuff buff) {
		this.buffs.add(buff);
		buff.init();
	}

	public static class ReceiveBuffReport {
		public SeatStageBuff buff;
		public StageBuff<?> rejectBy;
		public boolean accept;
		public static ReceiveBuffReport ofAccept(SeatStageBuff buff) {
			ReceiveBuffReport report = new ReceiveBuffReport();
			report.buff = Objects.requireNonNull(buff);
			report.accept = true;
			return report;
		}
		public static ReceiveBuffReport ofReject(SeatStageBuff buff, StageBuff<?> rejectBy) {
			ReceiveBuffReport report = new ReceiveBuffReport();
			report.buff = Objects.requireNonNull(buff);
			report.rejectBy = Objects.requireNonNull(rejectBy);
			report.accept = false;
			return report;
		}
	}

	/* **********
	 * Delegate *
	 ********** */

	public void putActionNow(StageAction action) {
		stage.timeline.putActionNow(action);
	}

	/* **********
	 *  Others  *
	 ********** */

	@Override
	public String toString() {
		final StringBuilder builder = new StringBuilder(100);

		appendStringWithoutFoe(builder);
		builder.append(StringUtils.join(", ", objects));

		return builder.toString();
	}

	public void appendStringWithoutFoe(StringBuilder builder) {
		builder.append('[').append(x).append(']');
	}
}
